/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.datatheorem.ohos.trustkit.asyntaskutils;

import static com.datatheorem.ohos.trustkit.asyntaskutils.TaskDispatcherEnum.GLOBAL;

import ohos.app.Context;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.app.dispatcher.task.Revocable;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.lang.ref.WeakReference;

/**
 * 消息分发者
 *
 * @param <T>
 * @since 2021-03-19
 */
public abstract class Distribution<T> {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0x00201, "My_Distribution");
    private WeakReference<Context> mContext = null;
    private TaskDispatcherCallBack callBack;

    private TaskDispatcherEnum normalTaskDispatcherType = GLOBAL;
    private TaskPriority taskPriority = TaskPriority.DEFAULT;

    private String dispatcherName = "mTaskDispatcher";
    private boolean isSync = false;
    private boolean isSyncByBarrier = false;
    private long delayTime = 0L;

    /**
     * 异步线程返回值(用于移除线程操作)
     **/
    private Revocable revocable = null;

    private Distribution() {
        throw new IllegalArgumentException("Null is not allowed");
    }

    /**
     * context初始化
     *
     * @param context
     **/
    public Distribution(Context context) {
        mContext = new WeakReference(context);
    }

    /**
     * 添加回调初始化
     *
     * @param context
     * @param callBack
     **/
    public Distribution(Context context, TaskDispatcherCallBack callBack) {
        mContext = new WeakReference(context);
        this.callBack = callBack;
    }

    /**
     * 回调和自定义参数类型回调
     *
     * @param context
     * @param callBack
     * @param t
     **/
    public Distribution(Context context, TaskDispatcherCallBack callBack, T t) {
        mContext = new WeakReference(context);
        this.callBack = callBack;
    }

    /**
     * 创建线程
     *
     * @return TaskDispatcher
     */
    private TaskDispatcher createTaskDispatcherByType() {
        synchronized (normalTaskDispatcherType) {
            switch (normalTaskDispatcherType) {
                case GLOBAL:
                    return mContext.get().getGlobalTaskDispatcher(taskPriority);
                case PARALLEL:
                    return mContext.get().createParallelTaskDispatcher(dispatcherName, taskPriority);
                case SERIAL:
                    return mContext.get().createSerialTaskDispatcher(dispatcherName, TaskPriority.DEFAULT);
                case MAIN:
                    return mContext.get().getMainTaskDispatcher();
                case UI:
                    return mContext.get().getUITaskDispatcher();
                default:
                    return null;
            }
        }
    }

    /**
     * 设置线程优先级
     *
     * @param taskPriority
     **/
    public synchronized void setTaskPriority(TaskPriority taskPriority) {
        this.taskPriority = taskPriority;
    }

    /**
     * 设置线程类型
     *
     * @param taskDispatcherEnum
     **/
    public synchronized void setTaskDispatcherType(TaskDispatcherEnum taskDispatcherEnum) {
        normalTaskDispatcherType = taskDispatcherEnum;
    }

    /**
     * 设置异步延迟时间,只用于异步延迟线程,
     * 设置该值线程就被自动替换为异步线程,
     * 如果需要同步需要调用cleanDelayTime清除延迟时间,setSync(true)
     *
     * @param delayTime
     */
    private synchronized void setDelayTime(long delayTime) {
        this.delayTime = delayTime;
        isSync = false;
    }

    /**
     * 清除延迟时间
     */
    private synchronized void cleanDelayTime() {
        this.delayTime = 0;
    }

    /**
     * 设置线程同步类型
     *
     * @param isSync
     */
    private synchronized void setSync(boolean isSync) {
        this.isSync = isSync;
    }

    /**
     * 设置屏障同步类型
     *
     * @param isSyncByBarrier 屏障是否同步
     */
    private synchronized void setSyncByBarrier(boolean isSyncByBarrier) {
        this.isSyncByBarrier = isSyncByBarrier;
    }

    /**
     * 取消异步线程任务 返回是否取消成功
     *
     * @return boolean
     * @throws Exception 抛出异常外部处理
     */
    public synchronized boolean revokeAsync() throws Exception {
        if (revocable != null) {
            return revocable.revoke();
        } else {
            return false;
        }
    }

    /**
     * 线程调度器开始执行方法
     *
     * @param t
     **/
    public void start(T t) {
        TaskDispatcher mTaskDispatcher = createTaskDispatcherByType();
        synchronized (mTaskDispatcher) {


            if (delayTime == 0) {
                if (isSync) {
                    mTaskDispatcher.syncDispatch(new Runnable() {
                        @Override
                        public synchronized void run() {
                            boolean isUi = mTaskDispatcher.equals(mContext.get().getUITaskDispatcher());
                            boolean isMain = mTaskDispatcher.equals(mContext.get().getMainTaskDispatcher());
                            HiLog.info(LABEL_LOG, "isUi = " + isUi + " ,isMain = " + isMain);
                            doSomething(t);
                            if (callBack != null) {
                                mContext.get().getUITaskDispatcher().syncDispatch(new Runnable() {
                                    @Override
                                    public synchronized void run() {
                                        callBack.onTaskDispatcherBack();
                                        HiLog.info(LABEL_LOG, "callBack.onTaskDispatcherBack()...");
                                    }
                                });
                            }
                        }
                    });
                } else {
                    revocable = mTaskDispatcher.asyncDispatch(new Runnable() {
                        @Override
                        public synchronized void run() {
                            doSomething(t);
                            if (callBack != null) {
                                mContext.get().getUITaskDispatcher().syncDispatch(new Runnable() {
                                    @Override
                                    public synchronized void run() {
                                        callBack.onTaskDispatcherBack();
                                    }
                                });
                            }
                        }
                    });
                }
            } else {
                revocable = mTaskDispatcher.delayDispatch(new Runnable() {
                    @Override
                    public synchronized void run() {
                        doSomething(t);
                        if (callBack != null) {
                            mContext.get().getUITaskDispatcher().syncDispatch(new Runnable() {
                                @Override
                                public synchronized void run() {
                                    callBack.onTaskDispatcherBack();
                                }
                            });
                        }
                    }
                }, delayTime);
            }
            setBarrier(mTaskDispatcher, isSyncByBarrier);
        }
    }

    /**
     * 屏障任务
     *
     * @param taskDispatcher
     * @param isSyncByBarrier
     */
    private void setBarrier(TaskDispatcher taskDispatcher, boolean isSyncByBarrier) {
        if (isSyncByBarrier) {
            taskDispatcher.syncDispatchBarrier(new Runnable() {
                @Override
                public synchronized void run() {
                    doByLast();
                    if (callBack != null) {
                        mContext.get().getUITaskDispatcher().syncDispatch(new Runnable() {
                            @Override
                            public synchronized void run() {
                                callBack.onTaskDispatcherDoLastBack();
                            }
                        });
                    }
                }
            });
        } else {
            taskDispatcher.asyncDispatchBarrier(new Runnable() {
                @Override
                public synchronized void run() {
                    doByLast();
                    if (callBack != null) {
                        mContext.get().getUITaskDispatcher().syncDispatch(new Runnable() {
                            @Override
                            public synchronized void run() {
                                callBack.onTaskDispatcherDoLastBack();
                            }
                        });
                    }
                }
            });
        }
    }

    /**
     * 线程异步处理
     *
     * @param t
     */
    public abstract void doSomething(T t);

    /**
     * 线程屏障事务
     */
    public void doByLast() {
    }
}
